Designs by Sabi*

DRAMATIZED CASE STUDY This is a reconstructed presentation of real product-design work. To protect confidentiality, the domain, data, terminology, visual details, and other identifying information have been abstracted or recreated as Meridian Commerce, a fictional retail platform. The design challenges, decisions, tradeoffs, and lessons reflect my actual work.

A loyalty system that helps multi-location retailers reward the right customer behavior, at two very different scales. I worked on the part of the product where a single promotion stops being a setting and starts being a plan: one idea that has to hold different shapes across regions and tiers, without turning into a pile of rules that no longer look related to each other.

The challenge was not simply to let people create promotions. It was to let one person add a simple points bump in under a minute, and let another configure a season’s worth of exceptions to that idea — different rewards by location, different rewards by tier — as one coherent plan, without treating the second as just a bigger, more tedious version of the first.


My Role

  • Product Design / UX
  • Research, interaction design, prototyping
  • Team: Worked in collaboration with one Product Manager and one Lead Developer.
  • Timeline: 3 weeks from request to design delivery. Implementation timeline TBD.

The Challenge

  • The existing flow was a single form, built initially for the quick, one-off rules
  • As the loyalty offering grew, planners needed to think in higher level, not individual rules: one promotional idea, expressed differently across regions and tiers, launched and managed together
  • A flat list of individual rules can’t hold that structure as nothing shows that five rules are actually one plan with variations and takes a long time for the user to add to the system
  • Both kinds of user still need to see everything in one shared place. A planner shouldn’t need a separate tool to check whether their new promotion conflicts with something already live

The product already had a simple, forgiving way to add one promotion. The challenge was giving planners a way to express a real strategy without flattening that plan into disconnected rules, or making the simple case carry weight it doesn’t need.

Finding the Problem

  • A planner doesn’t think in individual rules, they think in one program with exceptions. “3× points everywhere, but Atlanta North and Macon get 3× regardless of tier, and Platinum members get 5× everywhere”
  • Modeled as separate rules, that one idea becomes several disconnected rows
  • A quick user’s request never needs that structure at all: “double points today” has no exceptions, no plan behind it
  • Users were expending a massive amount of effort when trying to insert large scale strategies that with the current UI translated to be hundreds of rules when the reality was they needed a handful of base rules and a varying amount of exceptions.

Key insight: A planner’s real unit of work is the program, not the rule. Once a program can have exceptions, something has to decide what happens when two exceptions describe the same customer. While ideating the redesign, the Product Manager reported that the night before, a user spent many stressful hours trying to set the year’s strategy in place.


The Design Question

How might we let one promotional idea hold real exceptions without either the simple case or the exceptions turning into something that has to be queried instead of read?

Treating “add a promotion” and “plan a season” as different tools either duplicates the product or forces every user through the heavier one. They need one shared object, sized differently depending on how far someone takes it.


Exploring the Directions

I considered two structural approaches before arriving at the one I built.

Option 01 — Clone the quick form into a bulk-add tool

Duplicate a base rule across a list of locations or tiers, each becoming its own independent row.

Strength: small addition to the existing model, fast to ship Tradeoff: produces a pile of individually-editable rules with no memory that they came from one plan. It looks like it solves bulk creation, but it doesn’t hold a strategy — it just makes the first step faster.

Option 02 — Split the model: Rule and Promotion as separate objects

Two distinct entry points, two schemas — a fast, single-purpose Rule, and a Promotion built from a base plus variants.

Strength: matches how a planner actually thinks Tradeoff: pushes the hard question downstream. Once a Rule and a Promotion live in the same shared list, how does someone tell them apart without one object lying about its own complexity?

Where this landed

I built the actual list view before fully committing to Option 02, and it changed my mind about the shape of the solution.

Rule Programs list view in Meridian Commerce, showing programs with an exceptions column

The Rule Programs list — one object type, with an exceptions count doing the work of signaling complexity at a glance.

Option 03 — One object: a program, made of a base rule and its exceptions

Every promotion is a Rule Program: one base rule — a reward and a scope, written as a plain statement — plus an optional, repeatable set of exceptions, each of which can only narrow what the base already covers. A program with zero exceptions is exactly as fast to create as the old quick rule ever was. A program with a dozen exceptions is a real season strategy, built from the same object, read the same way.

This is the frame I want the rest of the case study to sit inside: not “quick vs. bulk,” but a ruleset made of one base and any number of exceptions to it — a shape flexible enough to be a five-second promotion or a season’s worth of planning, without ever needing to become two different tools.


Two Rules That Hold the Whole Model Together

Once a program can have more than one exception, two questions had to be answered for the model to stay trustworthy at any scale.

1. An exception can only narrow the base — never widen it

If the base rule applies to All Locations and All Tiers, an exception can scope down to Atlanta North, or to Platinum tier, or both. It can never introduce a location or tier the base didn’t already include. This keeps every exception provably part of the plan it belongs to, rather than a second, unrelated rule wearing the same wrapper.

2. Overlapping exceptions resolve top to bottom, first match wins

Two exceptions can describe the same customer — a Platinum member shopping at Atlanta North might match both a location-based exception and a tier-based one. Rather than introduce a second precedence system, exceptions resolve in list order: the first one that matches applies, and a customer who matches nothing falls back to the base rule.

This reuses a concept the product already needed one level up — active programs resolve conflicts by priority (“High” stops all other programs, “Medium” stops lower-priority ones, “Low” lets others continue). Exceptions now resolve conflicts the same way, one level down, inside a single program. One mental model, two scales — a planner who understands one already understands the other.

Making the ordering real: each exception in the repeater gets a drag handle, so reordering is setting precedence — there’s no separate priority field to fill in and forget to update. A neutral alert banner on the Earning Rules card states the rule plainly: exceptions are checked top to bottom, and the first match applies.


From Exploration to Action

Set the base rule as a statement (reward + scope) → add exceptions that narrow it, in priority order → set the program's own priority if it could conflict with another active program → launch → monitor

The list view carries its own monitoring: active programs, how many currently have exceptions, points awarded, and revenue influenced are all visible without opening anything.


What Changed

  • For users: a planner building a season’s worth of exceptions and a store-level user adding one quick promotion now use the same tool, sized to what they actually need
  • For the product: a single flexible object (program, base rule, exceptions) replaces what could have become two parallel systems

Measuring the work

This is still in progress, so there’s no shipped-metric evidence yet, but the list view already surfaces the metrics I’d want to watch: active programs, how many currently carry exceptions, points awarded, and revenue influenced. If I were instrumenting further, I’d add:

  • Time to create a program with zero exceptions (should stay effectively identical to the old quick-rule flow)
  • Ratio of programs with exceptions to programs without, over time (a proxy for whether the model is actually being used for real planning, not just simple cases with a new name)
  • Frequency of reordering after initial creation (a proxy for whether first-match-wins is intuitive, or whether planners keep needing to fix precedence after the fact)

What I Learned

Complexity is easier to make optional than to make separate. My instinct going in was: different amounts of complexity need different objects. What actually worked was the opposite. One object type, where complexity is something a program has (an exceptions count that can be zero) rather than something a separate program is. The “which kind of row is this” question disappeared because there was only ever one kind of row.